//=================================================================================//
// Main windows
// Author : Wang Yong
// Date   : 2020.10.28
//=================================================================================//
#include "MainWindow.h"
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#ifdef _WIN32
    #include <io.h>
    #include <direct.h>
#else
    #include <unistd.h>
    #include <sys/stat.h>
#endif
#include "vtkGenericOpenGLRenderWindow.h"
#include "DrawFarDlg.h"
#include "DrawNearDlg.h"
#include "DrawCurDlg.h"
#include "DrawNetDlg.h"
#include "GeomDialog.h"

using namespace std;

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    ui.setupUi(this);
	ui.projTree->expandAll();
	ui.actionDrawFars3D->setDisabled(true);
	ui.actionDrawFars2D->setDisabled(true);
	ui.actionDrawNear3D->setDisabled(true);
	ui.actionDrawMonRCS->setDisabled(true);
	ui.actionDrawNetPara->setDisabled(true);
	ui.actionDrawCurrent->setDisabled(true);
	m_CemExe = new QProcess(this);

	// connect the slot for main menu
	connect(ui.actionOpenProj, SIGNAL(triggered()), this, SLOT(fn_OpenProject()));
	connect(ui.actionSaveProj, SIGNAL(triggered()), this, SLOT(fn_SaveProject()));
	connect(ui.actionSave_As,  SIGNAL(triggered()), this, SLOT(fn_ProjSave_As()));
	connect(ui.actionClearProj,SIGNAL(triggered()), this, SLOT(fn_ClearProjct()));
	connect(ui.actionAboutVer, SIGNAL(triggered()), this, SLOT(fn_AboutWindow()));
	connect(ui.actionShowMesh, SIGNAL(triggered()), this, SLOT(fn_ShowHideMsh()));
	connect(ui.actionShowGrid, SIGNAL(triggered()), this, SLOT(fn_ShowGridMsh()));
	connect(ui.actionShowNode, SIGNAL(triggered()), this, SLOT(fn_ShowNodeMsh()));
	connect(ui.actionFitViews, SIGNAL(triggered()), this, SLOT(fn_FitViewMesh()));
	connect(ui.actionEnvirSet, SIGNAL(triggered()), this, SLOT(fn_ConfigMpich()));
	connect(ui.actionExit,     SIGNAL(triggered()), this, SLOT(close()));
	//
	connect(ui.actionCheckSet, SIGNAL(triggered()), this, SLOT(fn_RunPreCheck()));
	connect(ui.actionRun_OMP,  SIGNAL(triggered()), this, SLOT(fn_Run_OMP_Exe()));
	connect(ui.actionRun_MPI,  SIGNAL(triggered()), this, SLOT(fn_Run_MPI_Exe()));
	connect(m_CemExe, SIGNAL(readyRead()), this, SLOT(fn_ShowLogInfo()));

	// connect the slots for right click on the tree
	connect(ui.actionLoadMesh,    SIGNAL(triggered()), this, SLOT(fn_LoadOutMesh()));
	connect(ui.actionOutGiD_Mesh, SIGNAL(triggered()), this, SLOT(fn_OutGid_Mesh()));
	connect(ui.actionAddMaterial, SIGNAL(triggered()), this, SLOT(fn_AddMaterial()));
	connect(ui.actionAddPlanWave, SIGNAL(triggered()), this, SLOT(fn_AddPlanWave()));
	connect(ui.actionAddWirePort, SIGNAL(triggered()), this, SLOT(fn_AddWirePort()));
	connect(ui.actionAddLumpPort, SIGNAL(triggered()), this, SLOT(fn_AddLumpPort()));
	connect(ui.actionAddRectPort, SIGNAL(triggered()), this, SLOT(fn_AddRectPort()));
	connect(ui.actionAddCircPort, SIGNAL(triggered()), this, SLOT(fn_AddCircPort()));
	connect(ui.actionAddCoaxPort, SIGNAL(triggered()), this, SLOT(fn_AddCoaxPort()));
	connect(ui.actionAddEquivSrc, SIGNAL(triggered()), this, SLOT(fn_AddEquivSrc()));
	connect(ui.actionAddFarsSet,  SIGNAL(triggered()), this, SLOT(fn_AddFarsSets()));
	connect(ui.actionAddNearSet,  SIGNAL(triggered()), this, SLOT(fn_AddNearSets()));

	//
	connect(ui.projTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(fn_ShowRightMenu(const QPoint&)));
	connect(ui.projTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(fn_DoubleClicked(QTreeWidgetItem*, int)));
	connect(ui.projTree, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(fn_A_LeftClicked(QTreeWidgetItem*, int)));
	
	// connect the slots for post-process graph
	connect(ui.actionUpdatePost, SIGNAL(triggered()), this, SLOT(fn_SetPostData()));
	connect(ui.actionDrawFars3D, SIGNAL(triggered()), this, SLOT(fn_DrawFars3D()));
	connect(ui.actionDrawFars2D, SIGNAL(triggered()), this, SLOT(fn_DrawFars2D()));
	//connect(ui.actionDrawMonRCS, SIGNAL(triggered()), this, SLOT(add_MonRCS()));
	connect(ui.actionDrawNear3D, SIGNAL(triggered()), this, SLOT(fn_DrawNear3D()));
	connect(ui.actionDrawCurrent, SIGNAL(triggered()), this, SLOT(fn_DrawCurrent()));
	connect(ui.actionDrawNetPara, SIGNAL(triggered()), this, SLOT(fn_DrawNetPara()));
	connect(ui.actionInsertLine, SIGNAL(triggered()), this, SLOT(fn_InsertLine()));
	connect(ui.actionExportLine, SIGNAL(triggered()), this, SLOT(fn_ExportLine()));
	//connect(ui.actionExport_graph, SIGNAL(triggered()), this, SLOT(exportGraph()));
}

MainWindow::~MainWindow()
{
	delete m_pMediDialog;
	delete m_pWaveDialog;
	delete m_pWireDialog;
	delete m_pLumpDialog;
	delete m_pPortDialog;
	delete m_pEquiDialog;
	delete m_pSymmDialog;
	delete m_pFreqDialog;
	delete m_pSolvDialog;
	delete m_pFarsDialog;
	delete m_pNearDialog;
	delete m_pVoltDialog;
	delete m_pMpi_Config;
	delete m_qvtkMesh;
	m_CemExe->close();
	delete m_CemExe;
}

void MainWindow::fn_OpenProject()
{
	// get the project file name
	//auto option = QFileDialog::DontUseNativeDialog;
	auto fd = QFileDialog::getOpenFileName(this, "Open CEM project", "", "CEM File (*.xml)");
	auto iLen = fd.length();
	if (iLen < 1) return;
	// set path of project
	m_sProjName = fd.left(iLen - 4).toStdString();
	iLen = max(fd.lastIndexOf('/'), fd.lastIndexOf('\\')) + 1;
	if (iLen < 1) {
		m_sProjPath = "";
		m_sPostFile = "post/" + m_sProjName;
	} else {
		m_sProjPath = fd.left(iLen).toStdString();
		m_sPostFile = m_sProjPath + "post/" + m_sProjName.substr(iLen);
	}

	fn_ClearProjct();

	auto sName = fd.toStdString();
	if (!m_data.fn_LoadFile(sName)) {
		QMessageBox::information(this, "Warn", "Load project file failed.");
		return;
	}
	if (!m_data.fn_LoadNear(m_sProjPath)) {
		QMessageBox::information(this, "Warn", "Load near field mesh failed.");
		return;
	}
	
	fn_SetProjTree();
	QString sMsg = "Project :" + QString::fromStdString(m_sProjName) + " has been loaded.";
	ui.message->setText(sMsg);
	this->setWindowTitle(QString::fromStdString(m_sProjName));
	// set post data
	fn_SetPostData();

	std::string sMesh;
	if (m_data.m_solvSet.iAlgType == ALG_FEM) {
		sMesh = m_sProjPath + m_data.m_sMeshName + ".tet";
	} else {
		sMesh = m_sProjPath + m_data.m_sMeshName + ".gdm";
	}
	if (m_data.fn_LoadMesh(sMesh)) {
		QString sName = "Model";
		auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);
		for (int i = 0; i < p->childCount(); ++i) {
			if (p->child(i)->text(0) == "Mesh") {
				p = p->child(i);
				break;
			}
		}
		if (p->childCount() > 0) p->removeChild(p->child(0));
		// insert item in the tree
		auto son = new QTreeWidgetItem();
		son->setText(0, m_data.m_sMeshName.c_str());
		p->addChild(son);
		ui.actionCheckSet->setEnabled(true);
		ui.message->append(QString::fromStdString("Mesh File : " + sMesh + "\n"));
		ui.actionCheckSet->setEnabled(true);

		// draw the mesh object
		if (m_data.m_solvSet.iAlgType == ALG_FEM) {
			m_VtkObj.fn_CreateMap(m_data.m_volu);
		} else {
			m_VtkObj.fn_CreateMap(m_data.m_surf);
		}
		m_VtkObj.fn_UpdateMsh(m_qvtkMesh);
	} else {
		QMessageBox::information(this, "Warning", "Can't open the mesh file !");
	}
}

void MainWindow::fn_SetProjTree()
{
	QTreeWidgetItem *pRoot{ nullptr };
	QTreeWidgetItem *pNode{ nullptr };
	int i, j;
	auto p = ui.projTree->topLevelItem(0);
	for (i = 0; i < p->childCount(); ++i) {
		pRoot = p->child(i);
		if (pRoot->text(0) == "Model") {
			for (j = 0; j < pRoot->childCount(); ++j) {
				auto pGeom = pRoot->child(j);
				if (pGeom->text(0) == "Geometry") {
					for (auto &geom : m_data.m_vGeom) {
						pNode = new QTreeWidgetItem();
						pNode->setText(0, QString::fromStdString(geom.sName));
						pGeom->addChild(pNode);
					}
					break;
				}
			}
		} else if (pRoot->text(0) == "Material") {
			for (j = 2; j < m_data.m_vMedi.size(); ++j) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(m_data.m_vMedi[j].sName));
				pRoot->addChild(pNode);
			}
		} else if (pRoot->text(0) == "Source") {
			for (auto &src : m_data.m_vWave) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(src.sName));
				pRoot->child(0)->addChild(pNode);
			}
			for (auto &src : m_data.m_vWGap) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(src.sName));
				pRoot->child(1)->addChild(pNode);
			}
			for (auto &src : m_data.m_vLump) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(src.sName));
				pRoot->child(2)->addChild(pNode);
			}
			for (auto &src : m_data.m_vPort) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(src.sName));
				pRoot->child(3)->addChild(pNode);
			}
			for (auto &src : m_data.m_vEqui) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(src.sName));
				pRoot->child(4)->addChild(pNode);
			}
		} else if (pRoot->text(0) == "PostSetting") {
			for (auto &post : m_data.m_vFars) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(post.sName));
				pRoot->child(0)->addChild(pNode);
			}
			for (auto &post : m_data.m_vNear) {
				pNode = new QTreeWidgetItem();
				pNode->setText(0, QString::fromStdString(post.sName));
				pRoot->child(1)->addChild(pNode);
			}
			if (m_data.m_bCalcCurr) {
				pRoot->child(2)->setCheckState(0, Qt::CheckState::Checked);
			} else {
				pRoot->child(2)->setCheckState(0, Qt::CheckState::Unchecked);
			}
		}
	}
}

void MainWindow::fn_SaveProject()
{
	// check is first time save
	if (m_sProjName.empty()) {
		fn_ProjSave_As();
	} else {
		fn_WriteProjFile();
	}
}

void MainWindow::fn_ProjSave_As()
{
	// get the project file name
	auto fd = QFileDialog::getSaveFileName(this, "Save CEM project", "", "CEM File (*.xml)");
	auto iLen = fd.length();
	if (iLen < 1) return;

	m_sProjName = fd.left(iLen - 4).toStdString();
	iLen = max(fd.lastIndexOf('/'), fd.lastIndexOf('\\')) + 1;

#ifdef _WIN32
	if (_access(fd.toStdString().c_str(), 0) == -1) {    // if not exist
#else
	if (access(fd.toStdString().c_str(), 0) == -1) {     // if not exist
#endif
		// get project path
		string sName = m_sProjName.substr(iLen);
		m_sProjPath = m_sProjName + "/";
		m_sProjName = m_sProjPath + sName;
		string sPost = m_sProjPath + "/post";
		m_sPostFile = sPost + "/" + sName;
#ifdef _WIN32
		_mkdir(m_sProjPath.c_str());
		_mkdir(sPost.c_str());
#else
		mkdir(m_sProjPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
		mkdir(sPost.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
	} else {
		if (iLen < 1) {
			m_sProjPath = "";
	    } else {
			m_sProjPath = fd.left(iLen).toStdString();
		}
	}

	fn_WriteProjFile();
	this->setWindowTitle(QString::fromStdString(m_sProjName));
}

void MainWindow::fn_WriteProjFile()
{
	QString sName = "PostSetting";
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);

	if (Qt::CheckState::Checked == p->child(2)->checkState(0)) {
		m_data.m_bCalcCurr = true;
	} else {
		m_data.m_bCalcCurr = false;
	}

	if (!m_data.fn_WriteXML(m_sProjName)) {
		QMessageBox::warning(this, "Warning", "Save project failed.");
		return;
	}
	if (!m_data.fn_SaveMesh(m_sProjPath)) {
		QMessageBox::warning(this, "Warning", "Save mesh file failed.");
	}
	m_data.fn_SaveNear(m_sProjPath);

	ui.message->append("Project has been saved.");
}

void MainWindow::fn_ClearProjct()
{
	m_data.fn_ClearData();
	m_post.fn_ClearData();
	m_vPhoto2D.clear();
	m_vPhoto3D.clear();
	for (auto i = ui.drawPad->count() - 1; i > 0; --i) {
		ui.drawPad->removeTab(i);
	}
	auto p = ui.projTree->topLevelItem(0);
	QTreeWidgetItem *me;
	for (auto i = 0; i < p->childCount(); ++i) {
		if ("Model" == p->child(i)->text(0)) {
			for (int j = 0; j < p->child(i)->childCount(); ++j) {
				fn_ClearTreeNode(p->child(i)->child(j));
			}
			m_VtkObj.fn_ClearMesh(m_qvtkMesh);
		} else if ("Material" == p->child(i)->text(0)) {
			me = p->child(i);
			while (me->childCount() > 2) {
				me->removeChild(me->child(1));
			}
		} else if ("Source" == p->child(i)->text(0)) {
			fn_ClearTreeNode(p->child(i)->child(0));    // clean plane wave
			fn_ClearTreeNode(p->child(i)->child(1));    // clean wire port
			fn_ClearTreeNode(p->child(i)->child(2));    // clean lump port
			fn_ClearTreeNode(p->child(i)->child(3));    // clean wave port
			fn_ClearTreeNode(p->child(i)->child(4));    // clean equivalent source
		} else if ("PostSetting" == p->child(i)->text(0)) {
			fn_ClearTreeNode(p->child(i)->child(0));    // clean far field set
			fn_ClearTreeNode(p->child(i)->child(1));    // clean near field set
			p->child(i)->child(2)->setCheckState(0, Qt::CheckState::Unchecked);  // clear current setting
		} else if ("Result" == p->child(i)->text(0)) {
			fn_ClearTreeNode(p->child(i)->child(0));    // clean far field
			fn_ClearTreeNode(p->child(i)->child(1));    // clean near field
			fn_ClearTreeNode(p->child(i)->child(2));    // clean current
			fn_ClearTreeNode(p->child(i)->child(3));    // clean network parameter
		}
	}
}

void MainWindow::fn_LoadOutMesh()
{
	// get the mesh file name
	QString sMesh;
	if (m_data.m_solvSet.iAlgType == ALG_FEM) {
		sMesh = QFileDialog::getOpenFileName(this, "Load mesh", "", "Mesh Files (*.tet)");
	} else {
		sMesh = QFileDialog::getOpenFileName(this, "Load mesh", "", "Mesh Files (*.gdm *.ocm)");
	}
	if (sMesh.length() < 1) return;

	QString sName = "Model";
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);
	for (int i = 0; i < p->childCount(); ++i) {
		if (p->child(i)->text(0) == "Mesh") {
			p = p->child(i);
			break;
		}
	}
	if (p->childCount() > 0) {
		auto rb = QMessageBox::question(this,"Warning","Do you want to replace the mesh file?",
			QMessageBox::Yes | QMessageBox::Yes, QMessageBox::No);
		if (rb == QMessageBox::No) return;
	}

	int iLen = std::max(sMesh.lastIndexOf('/'), sMesh.lastIndexOf('\\')) + 1;
	m_data.m_sMeshName = sMesh.mid(iLen).toStdString();
	iLen = m_data.m_sMeshName.length() - 4;
	m_data.m_sMeshName = m_data.m_sMeshName.substr(0, iLen);

	string sFile = sMesh.toStdString();
	if (m_data.fn_LoadMesh(sFile)) {
		// insert item in the tree
		auto son = new QTreeWidgetItem();
		if (p->childCount() > 0) p->removeChild(p->child(0));
		son->setText(0, m_data.m_sMeshName.c_str());
		p->addChild(son);
		sName = "Model";
		auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);
		for (int i = 0; i < p->childCount(); ++i) {
			if (p->child(i)->text(0) == "Geometry") {
				p = p->child(i);
				break;
			}
		}

		while (p->childCount() > 0) {
			p->removeChild(p->child(0));
		}
		for (auto &geo : m_data.m_vGeom) {
			son = new QTreeWidgetItem();
			son->setText(0, geo.sName.c_str());
			p->addChild(son);
		}

		ui.message->setText("Mesh File : " + sMesh + "\n");
		ui.actionCheckSet->setEnabled(true);

		// draw the mesh object
		if (m_data.m_solvSet.iAlgType == ALG_FEM) {
			m_VtkObj.fn_CreateMap(m_data.m_volu);
		} else {
			m_VtkObj.fn_CreateMap(m_data.m_surf);
		}
		m_VtkObj.fn_UpdateMsh(m_qvtkMesh);
	} else {
		QMessageBox::information(this, "Warning", "Can't open the mesh file !");
	}
}

void MainWindow::fn_OutGid_Mesh()
{
	// get the project file name
	auto fd = QFileDialog::getSaveFileName(this, "Export GiD Mesh", "", "Mesh File (*.msh)");
	auto iLen = fd.length();
	if (iLen < 1) return;

	string sMshName = fd.toStdString();

	if (!m_data.fn_ExportGidMsh(sMshName)) {
		QMessageBox::warning(this, "Warning", "Export GiD mesh file failed.");
	}
}

// pop the menu of right click
void MainWindow::fn_ShowRightMenu(const QPoint &pos)
{
	QTreeWidgetItem *curItem = ui.projTree->itemAt(pos);      // get the node which is right clicked
	if (curItem == NULL) return;                  // position not inside projTree

	curItem = ui.projTree->currentItem();
	if (curItem->parent() == NULL){
		return;
	}

	QString sName = curItem->text(0);
	QMenu popMenu;                                // define menu for right click
	if ("Mesh" == sName) {
		popMenu.addAction(ui.actionLoadMesh);     // insert QAction into menu
		popMenu.addAction(ui.actionOutGiD_Mesh);  // insert QAction into menu
	} else if ("Material" == sName) {
		popMenu.addAction(ui.actionAddMaterial);
	} else if ("PlaneWave" == sName) {
		popMenu.addAction(ui.actionAddPlanWave);
	} else if ("WirePort" == sName) {
		popMenu.addAction(ui.actionAddWirePort);
	} else if ("LumpPort" == sName) {
		popMenu.addAction(ui.actionAddLumpPort);
	} else if ("WavePort" == sName) {
		popMenu.addAction(ui.actionAddRectPort);
		popMenu.addAction(ui.actionAddCircPort);
		popMenu.addAction(ui.actionAddCoaxPort);
	} else if ("ExterSource" == sName) {
		popMenu.addAction(ui.actionAddEquivSrc);
	} else if ("Set Far Field" == sName) {
		popMenu.addAction(ui.actionAddFarsSet);
	} else if ("Result" == sName) {
		popMenu.addAction(ui.actionUpdatePost);  
	} else if ("Set Near Field" == sName) {
		popMenu.addAction(ui.actionAddNearSet); 
	} else if ("Far Field" == sName) {
		popMenu.addAction(ui.actionDrawFars3D);  
		popMenu.addAction(ui.actionDrawFars2D);  
		popMenu.addAction(ui.actionDrawMonRCS);
	} else if ("Near Field" == sName) {
		popMenu.addAction(ui.actionDrawNear3D);     
	} else if ("Current" == sName) {
		popMenu.addAction(ui.actionDrawCurrent);    
	} else if ("Network Parameter" == sName) {
		popMenu.addAction(ui.actionDrawNetPara);    
	} else {
		return;
	}
	// pop menu to position where clicked.
	popMenu.exec(QCursor::pos());               
	curItem->setSelected(false);
}

// Double click the node in main tree
void MainWindow::fn_DoubleClicked(QTreeWidgetItem *treeNode, int n)
{
	auto p = treeNode->parent();
	if (NULL == p) return;

	string sName = treeNode->text(0).toStdString();
	if ("WireRadius" == sName) {
		if (!m_data.m_surf.vvWire.empty()) {
			QString sRad = QString::number(m_data.m_surf.vRadius[0]*1000); // m to mm
			bool bOK;
			QString text;
			text = QInputDialog::getText(this, tr("Set Radius"), tr("Input radius (mm)"), QLineEdit::Normal, sRad, &bOK);
			if (bOK && !text.isEmpty()) {
				double dRad = text.toDouble();
				m_data.fn_UpdateRadius(dRad);
			}
		}
	} else if ("Symmetry" == sName) {
		if (m_pSymmDialog == nullptr) {
			m_pSymmDialog = new SymmDialog(this);
		}
		m_pSymmDialog->fn_SetSymmetry(m_data.m_pSymSet);
	} else if ("Frequency" == sName) {
		if (m_pFreqDialog == nullptr) {
			m_pFreqDialog = new FreqDialog(this);
		}
		m_pFreqDialog->fn_SetFrequency(m_data.m_freqSet);
	} else if ("Solver" == sName) {
		if (m_pSolvDialog == nullptr) {
			m_pSolvDialog = new SolvDialog(this);
		}
		m_pSolvDialog->fn_SetSolver(m_data.m_solvSet);
	} else if ("Material" == p->text(0)) {
		for (int i = 0; i < m_data.m_vMedi.size(); ++i) {
			if (m_data.m_vMedi[i].sName == sName) {
				if (m_pMediDialog == nullptr) {
					m_pMediDialog = new MediDialog(this);
				}
				m_pMediDialog->fn_EditMaterial(m_data.m_vMedi, treeNode, i);
				break;
			}
		}
	} else if ("Geometry" == p->text(0)) {
		for (auto &geom : m_data.m_vGeom) {
			if (geom.sName == sName) {
				if (m_pGeomDialog == nullptr) {
					m_pGeomDialog = new GeomDialog(this);
				}
				m_pGeomDialog->fn_SetGeomMedi(m_data.m_vMedi, treeNode, &geom);
				break;
			}
		}
	} else if ("PlaneWave" == p->text(0)) {
		for (int i = 0; i < m_data.m_vWave.size(); ++i){
			if (m_data.m_vWave[i].sName == sName) {
				if (m_pWaveDialog == nullptr) {
					m_pWaveDialog = new WaveDialog(this);
				}
				m_pWaveDialog->fn_EditPlaneWave(m_data.m_vWave, treeNode, i);
				break;
			}
		}
	} else if ("WirePort" == p->text(0)) {
		for (int i = 0; i < m_data.m_vWGap.size(); ++i) {
			if (m_data.m_vWGap[i].sName == sName) {
				if (m_pWireDialog == nullptr) {
					m_pWireDialog = new WireDialog(this);
				}
				m_pWireDialog->fn_EditWirePort(m_data.m_vWGap, treeNode, i);
				break;
			}
		}
	} else if ("LumpPort" == p->text(0)) {
		for (int i = 0; i < m_data.m_vLump.size(); ++i) {
			if (m_data.m_vLump[i].sName == sName) {
				if (m_pLumpDialog == nullptr) {
					m_pLumpDialog = new LumpDialog(this);
				}
				m_pLumpDialog->fn_EditLumpPort(m_data.m_vLump, treeNode, i);
				break;
			}
		}
	} else if ("WavePort" == p->text(0)) {
		for (int i = 0; i < m_data.m_vPort.size(); ++i) {
			if (m_data.m_vPort[i].sName == sName) {
				if (m_pPortDialog == nullptr) {
					m_pPortDialog = new PortDialog(this);
				}
				m_pPortDialog->fn_EditWavePort(m_data.m_vPort, treeNode, i);
				break;
			}
		}
	} else if ("ExterSource" == p->text(0)) {
		for (int i = 0; i < m_data.m_vEqui.size(); ++i) {
			if (m_data.m_vEqui[i].sName == sName) {
				if (m_pEquiDialog == nullptr) {
					m_pEquiDialog = new EquiDialog(this);
				}
				m_pEquiDialog->fn_EditEquivSrc(m_data.m_vEqui, treeNode, i);
				break;
			}
		}
	} else if ("Voltage" == sName) {
		if (m_pVoltDialog != nullptr) {
			delete m_pVoltDialog;
		}
		m_pVoltDialog = new VoltDialog(this);
		m_pVoltDialog->fn_ShowVoltage(m_data);
	} else if ("Set Far Field" == p->text(0)) {
		for (int i = 0; i < m_data.m_vFars.size(); ++i) {
			if (m_data.m_vFars[i].sName == sName) {
				if (m_pFarsDialog == nullptr) {
					m_pFarsDialog = new FarsDialog(this);
				}
				m_pFarsDialog->fn_EditFarSet(m_data.m_vFars, treeNode, i);
				break;
			}
		}
	} else if ("Set Near Field" == p->text(0)) {
		for (int i = 0; i < m_data.m_vNear.size(); ++i) {
			if (m_data.m_vNear[i].sName == sName) {
				if (m_pNearDialog == nullptr) {
					m_pNearDialog = new NearDialog(this);
				}
				m_pNearDialog->fn_EditNearSet(m_data.m_vNear, treeNode, i);
				break;
			}
		}
	}
}

// left click the graph table and switch the displayed graph
void MainWindow::fn_A_LeftClicked(QTreeWidgetItem *treeNode, int n)
{
	auto p = treeNode->parent();
	if (NULL == p) return;

	QString sType = p->text(0);
	QString sName = treeNode->text(0);
	if ("Mesh" == sType){
		if (ui.drawPad->count() > 0) {
			ui.drawPad->setCurrentIndex(0);
		}
	} else if ("Far Field" == sType) {
		for (int i = 1; i < ui.drawPad->count(); ++i) {
			if (ui.drawPad->tabText(i) == sName) {
				ui.drawPad->setCurrentIndex(i);
				break;
			}
		}
	} else if ("Near Field" == sType) {
		for (int i = 1; i < ui.drawPad->count(); ++i) {
			if (ui.drawPad->tabText(i) == sName) {
				ui.drawPad->setCurrentIndex(i);
				break;
			}
		}
	} else if ("Current" == sType) {
		for (int i = 1; i < ui.drawPad->count(); ++i) {
			if (ui.drawPad->tabText(i) == sName) {
				ui.drawPad->setCurrentIndex(i);
				break;
			}
		}
	} else if ("Network Parameter" == sType) {
		for (int i = 1; i < ui.drawPad->count(); ++i) {
			if (ui.drawPad->tabText(i) == sName) {
				ui.drawPad->setCurrentIndex(i);
				break;
			}
		}
	}
}

void MainWindow::fn_AddMaterial()
{
	if (m_pMediDialog == nullptr) {
		m_pMediDialog = new MediDialog(this);
	}
	QString sName = "Material";
	auto pRoot = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);
	if (pRoot == nullptr) return;

	m_pMediDialog->fn_AddMaterial(m_data.m_vMedi, pRoot);
}

void MainWindow::fn_AddPlanWave()
{
	if (m_pWaveDialog == nullptr) {
		m_pWaveDialog = new WaveDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find plane wave
	auto pNode = fn_FindTreeNode(pSrc, "PlaneWave");
	if (pNode == nullptr) return;

	m_pWaveDialog->fn_AddPlaneWaves(m_data.m_vWave, pNode);
}

void MainWindow::fn_AddWirePort()
{
	if (m_pWireDialog == nullptr) {
		m_pWireDialog = new WireDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find wire port
	auto pNode = fn_FindTreeNode(pSrc, "WirePort");
	if (pNode == nullptr) return;

	m_pWireDialog->fn_AddWirePorts(m_data.m_vWGap, pNode);
}

void MainWindow::fn_AddLumpPort()
{
	if (m_pLumpDialog == nullptr) {
		m_pLumpDialog = new LumpDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find lump port
	auto pNode = fn_FindTreeNode(pSrc, "LumpPort");
	if (pNode == nullptr) return;

	m_pLumpDialog->fn_AddLumpPorts(m_data.m_vLump, pNode);
}

void MainWindow::fn_AddRectPort()
{
	if (m_pPortDialog == nullptr) {
		m_pPortDialog = new PortDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find waveport
	auto pNode = fn_FindTreeNode(pSrc, "WavePort");
	if (pNode == nullptr) return;

	m_pPortDialog->fn_AddWavePorts(m_data.m_vPort, pNode, "Rectangle");
}

void MainWindow::fn_AddCircPort()
{
	if (m_pPortDialog == nullptr) {
		m_pPortDialog = new PortDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find waveport
	auto pNode = fn_FindTreeNode(pSrc, "WavePort");
	if (pNode == nullptr) return;

	m_pPortDialog->fn_AddWavePorts(m_data.m_vPort, pNode, "Circular");
}

void MainWindow::fn_AddCoaxPort()
{
	if (m_pPortDialog == nullptr) {
		m_pPortDialog = new PortDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find waveport
	auto pNode = fn_FindTreeNode(pSrc, "WavePort");
	if (pNode == nullptr) return;

	m_pPortDialog->fn_AddWavePorts(m_data.m_vPort, pNode, "Coaxial");
}

void MainWindow::fn_AddEquivSrc()
{
	if (m_pEquiDialog == nullptr) {
		m_pEquiDialog = new EquiDialog(this);
	}
	auto pSrc = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Source");
	// find waveport
	auto pNode = fn_FindTreeNode(pSrc, "ExterSource");
	if (pNode == nullptr) return;

	m_pEquiDialog->fn_AddEquivSrc(m_data.m_vEqui, pNode);
}

void MainWindow::fn_AddFarsSets()
{
	if (m_pFarsDialog == nullptr) {
		m_pFarsDialog = new FarsDialog(this);
	}
	auto pPost = fn_FindTreeNode(ui.projTree->topLevelItem(0), "PostSetting");
	// find far field setting
	auto pRoot = fn_FindTreeNode(pPost, "Set Far Field");
	if (pRoot == nullptr) return;

	m_pFarsDialog->fn_AddFarsSet(m_data.m_vFars, pRoot);
}

void MainWindow::fn_AddNearSets()
{
	if (m_pNearDialog == nullptr) {
		m_pNearDialog = new NearDialog(this);
	}
	auto pPost = fn_FindTreeNode(ui.projTree->topLevelItem(0), "PostSetting");
	// find near field setting
	auto pRoot = fn_FindTreeNode(pPost, "Set Near Field");
	if (pRoot == nullptr) return;

	m_pNearDialog->fn_AddNearSet(m_data.m_vNear, pRoot);
}

void MainWindow::fn_AboutWindow()
{
	QMessageBox::information(this, "Info", "The software works for CEM pre-process setting and post-process display using QT and VTK.\n\t\tAuthor : Wang Yong @2020");
}

void MainWindow::fn_ShowHideMsh()
{
	int iGrf = ui.drawPad->currentIndex();
	if (iGrf == 0) {
		m_VtkObj.fn_ShowHideMsh();
		m_qvtkMesh->GetRenderWindow()->Render();
	} else {
		auto sName = ui.drawPad->tabText(iGrf);
		for (auto &grf : m_vPhoto3D) {
			if (grf->fn_GetName() == sName) {
				grf->fn_ShowHideMsh();
				grf->fn_GetVTKWidget()->GetRenderWindow()->Render();
				break;
			}
		}
	}
}

void MainWindow::fn_ShowGridMsh()
{
	int iGrf = ui.drawPad->currentIndex();
	if (iGrf == 0) {
		m_VtkObj.fn_ShowGridMsh();
		m_qvtkMesh->GetRenderWindow()->Render();
	} else {
		auto sName = ui.drawPad->tabText(iGrf);
		for (auto &grf : m_vPhoto3D) {
			if (grf->fn_GetName() == sName) {
				grf->fn_ShowGridMsh();
				grf->fn_GetVTKWidget()->GetRenderWindow()->Render();
				break;
			}
		}
	}
}

void MainWindow::fn_ShowNodeMsh()
{
	int iGrf = ui.drawPad->currentIndex();
	if (iGrf == 0) {
		m_VtkObj.fn_ShowNodeMsh();
		m_qvtkMesh->GetRenderWindow()->Render();
	} else {
		auto sName = ui.drawPad->tabText(iGrf);
		for (auto &grf : m_vPhoto3D) {
			if (grf->fn_GetName() == sName) {
				grf->fn_ShowNodeMsh();
				grf->fn_GetVTKWidget()->GetRenderWindow()->Render();
				break;
			}
		}
	}
}

void MainWindow::fn_FitViewMesh()
{
	auto sName = ui.drawPad->tabText(ui.drawPad->currentIndex());
	if (sName == "Model") {
		m_VtkObj.m_render->ResetCamera();
		m_qvtkMesh->GetRenderWindow()->Render();
		return;
	}
	for (auto i = 0; i < m_vPhoto3D.size(); ++i) {
		if (m_vPhoto3D[i]->fn_GetName() == sName) {
			m_vPhoto3D[i]->fn_FitView();
			break;
		}
	}
}

void MainWindow::fn_ConfigMpich()
{
	if (m_pMpi_Config == nullptr) {
		m_pMpi_Config = new Mpi_Config;
	}
	m_pMpi_Config->fn_ShowMpiDialog();
}

void MainWindow::fn_RunPreCheck()
{
	// check whether has mesh
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Model");
	p = p->child(0);
	
	if (p->childCount() == 0) {
		QMessageBox::information(this, "Info", "There is no mesh file.");
	}

	QString sLog;
	if (!m_data.fn_CheckData(sLog)) {
		QMessageBox::information(this, "Info", sLog);
		return;
	}

	// output the project file
	fn_SaveProject();
	if (!m_data.fn_OutNearPoint(m_sProjName)) {
		QMessageBox::information(this, "Info", "Write the near field point failed.");
	}

	QStringList arg;
	arg << QString::fromStdString(m_sProjName);

	m_CemExe->close();
	m_CemExe->start("bin/Pre_CEM", arg);
	m_CemExe->waitForReadyRead();
}

void MainWindow::fn_Run_OMP_Exe()
{
	QString sBin[] = { "bin/dPTGM","bin/dPFMM","bin/dPFEM","bin/dPUTD","bin/dPO","bin/dPBGM" };
	QString sSub[] = { ".tgm",".fmm",".fem",".utd",".po",".bgm" };

	int iAlg = m_data.m_solvSet.iAlgType;
	auto sFile = QString::fromStdString(m_sProjName) + sSub[iAlg];
	QFile p(sFile);
	if (!p.exists()) {
		QMessageBox::information(this, "Info", "Please run check project setting first.");
		return;
	}

	m_post.fn_ClearData();
	fn_SetPostData();

	QStringList arg;
	arg << QString::fromStdString(m_sProjName);
	m_CemExe->close();
	m_CemExe->start(sBin[iAlg], arg);
	m_CemExe->waitForReadyRead();
}

void MainWindow::fn_Run_MPI_Exe()
{
	QString sBin[] = { "bin/dPTGM","bin/dPFMM","bin/dPFEM","bin/dPUTD","bin/dPO","bin/dPBGM" };
	QString sSub[] = { ".tgm",".fmm",".fem",".utd",".po",".bgm" };

	int iAlg = m_data.m_solvSet.iAlgType;
	auto sFile = QString::fromStdString(m_sProjName) + sSub[iAlg];
	QFile p(sFile);
	if (!p.exists()) {
		QMessageBox::information(this, "Info", "Please run check project setting first.");
		return;
	}

	m_post.fn_ClearData();
	fn_SetPostData();

	QStringList arg;
	arg << QString::fromStdString(m_sProjName);
	m_CemExe->close();
	m_CemExe->start(sBin[iAlg], arg);
	m_CemExe->waitForReadyRead();
}

void MainWindow::fn_ShowLogInfo()
{
	ui.message->append(m_CemExe->readAll().data());
	ui.message->moveCursor(QTextCursor::End);
}

// deal with delete key press
void MainWindow::keyPressEvent(QKeyEvent *key)
{
	if (key->key() == Qt::Key_Delete) {
		// delete element in tree
		auto me = ui.projTree->currentItem();
		auto pParent = me->parent();
		if (NULL == pParent) return;

		std::string sName = me->text(0).toStdString();
		if ("Mesh" == pParent->text(0)) {
			m_data.m_sMeshName = "";
			m_VtkObj.fn_ClearMesh(m_qvtkMesh);
			pParent->removeChild(me);
		} else if ("Material" == pParent->text(0)) {
			if ("PEC" == sName) {
				QMessageBox::information(this, "Warn", "Can't delete PEC material.");
				return;
			}
			if ("Vacuum" == sName) {
				QMessageBox::information(this, "Warn", "Can't delete background material.");
				return;
			}
			m_data.fn_RemoveMedi(sName);
			pParent->removeChild(me);
		} else if ("Set Far Field" == pParent->text(0)) {
			m_data.fn_RemoveFars(sName);
			pParent->removeChild(me);
		} else if ("Set Near Field" == pParent->text(0)) {
			m_data.fn_RemoveNear(sName);
			pParent->removeChild(me);
		} else if ("PlaneWave" == pParent->text(0)) {
			m_data.fn_RemoveWave(sName);
			pParent->removeChild(me);
		} else if ("WirePort" == pParent->text(0)) {
			m_data.fn_RemoveWGap(sName);
			pParent->removeChild(me);
		} else if ("LumpPort" == pParent->text(0)) {
			m_data.fn_RemoveLump(sName);
			pParent->removeChild(me);
		} else if ("WavePort" == pParent->text(0)) {
			m_data.fn_RemovePort(sName);
			pParent->removeChild(me);
		} else if ("ExterSource" == pParent->text(0)) {
			m_data.fn_RemoveEqui(sName);
			pParent->removeChild(me);
		} else if ("Far Field" == pParent->text(0)) {
			fn_RemoveWindow(sName);
			fn_RemoveGraph2(sName);
			fn_RemoveGraph3(sName);
			pParent->removeChild(me);
		} else if ("Near Field" == pParent->text(0)) {
			fn_RemoveWindow(sName);
			fn_RemoveGraph3(sName);
			pParent->removeChild(me);
		} else if ("Current" == pParent->text(0)) {
			fn_RemoveWindow(sName);
			fn_RemoveGraph3(sName);
			pParent->removeChild(me);
		} else if ("Network Parameter" == pParent->text(0)) {
			fn_RemoveWindow(sName);
			fn_RemoveGraph2(sName);
			pParent->removeChild(me);
		}
	}
}

void MainWindow::fn_ShowVtkWidget()
{
	m_qvtkMesh = new QVTKOpenGLNativeWidget(ui.model);
	ui.ModelGrid->addWidget(m_qvtkMesh, 0, 0, 1, 1);

	m_VtkObj.fn_RenderWin(m_qvtkMesh);
}

void MainWindow::fn_ClearTreeNode(QTreeWidgetItem *p)
{
	while (p->childCount() > 0) {
		p->removeChild(p->child(0));
	}
}

void MainWindow::fn_RemoveWindow(std::string &sName)
{
	// delete graph
	for (int i = 0; i < ui.drawPad->count(); ++i) {
		if (ui.drawPad->tabText(i).toStdString() == sName) {
			ui.drawPad->removeTab(i);
			break;
		}
	}
}

void MainWindow::fn_RemoveGraph2(std::string &sName)
{
	// delete data
	auto iter = m_vPhoto2D.begin();
	while (iter != m_vPhoto2D.end()) {
		QString sGraph = (*iter)->fn_GetName();
		if (sGraph.toStdString() == sName) {
			(*iter)->fn_Clear();
			m_vPhoto2D.erase(iter);
			break;
		}
		++iter;
	}
}

void MainWindow::fn_RemoveGraph3(std::string &sName)
{
	// delete data
	auto iter = m_vPhoto3D.begin();
	while (iter != m_vPhoto3D.end()) {
		QString sGraph = (*iter)->fn_GetName();
		if (sGraph.toStdString() == sName) {
			(*iter)->fn_Clear();
			m_vPhoto3D.erase(iter);
			break;
		}
		++iter;
	}
}

void MainWindow::fn_SetPostData()
{
	QString sName = "PostSetting";
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), sName);

	if (Qt::CheckState::Checked == p->child(2)->checkState(0)) {
		m_data.m_bCalcCurr = true;
	} else {
		m_data.m_bCalcCurr = false;
	}

	m_post.fn_ClearData();
	m_post.fn_SetPostData(m_data);

	// respond to draw far field
	if (m_data.m_vFars.empty()) {
		ui.actionDrawFars3D->setDisabled(true);
		ui.actionDrawFars2D->setDisabled(true);
	} else {
		ui.actionDrawFars3D->setEnabled(true);
		ui.actionDrawFars2D->setEnabled(true);
	}
	// respond to draw near field
	if (m_data.m_vNear.empty()) {
		ui.actionDrawNear3D->setDisabled(true);
	} else {
		ui.actionDrawNear3D->setEnabled(true);
	}
	// respond to draw RCS
	if (m_data.m_vWave.empty()) {
		ui.actionDrawMonRCS->setDisabled(true);
	} else {
		ui.actionDrawMonRCS->setEnabled(true);
	}
	// respond to draw network parameter
	if (m_post.m_bNet) {
		ui.actionDrawNetPara->setEnabled(true);
	} else {
		ui.actionDrawNetPara->setDisabled(true);
	}
	// respond to draw current
	if (m_data.m_bCalcCurr) {
		ui.actionDrawCurrent->setEnabled(true);
	} else {
		ui.actionDrawCurrent->setDisabled(true);
	}
}

void MainWindow::fn_DrawFars3D()
{
	auto pDraw = new DrawFarDlg(false, this);
	FarSet para;
	pDraw->fn_ShowDialog(m_post, para);
	delete pDraw;
	if (para.bCancel) return;
	// load far field data
	if (!m_post.fn_LoadOneFar(m_sPostFile, para.iVoltIdx)) {
		QMessageBox::information(this, "Info", "Load far field file failed.");
		return;
	}
	// insert node in project tree
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Result");
	p = p->child(0);
	QString sName = fn_GetGraphName("3DFar", p);
	auto pSon = new QTreeWidgetItem();
	pSon->setText(0, sName);
	p->addChild(pSon);

	// insert tab in window
	int n = ui.drawPad->count();
	auto grf = new VtkImage3D(sName);
	ui.drawPad->addTab(grf->fn_GetTab(), sName);
	ui.drawPad->setCurrentIndex(n);

	// insert object in graph
	if (m_data.m_solvSet.iAlgType == ALG_FEM) {
		grf->fn_AddTetMsh(m_VtkObj.fn_GetTetMesh());
	} else {
		grf->fn_AddGdmMsh(m_VtkObj.fn_GetGdmMesh());
	}

	// create map
	float rad = m_VtkObj.m_radius;
	grf->fn_SetMapper(m_post.fn_CreateMapper(para, m_VtkObj.m_Oc, rad, 50.0));

	m_vPhoto3D.push_back(grf);
	grf->fn_ToRender();
}

void MainWindow::fn_DrawFars2D()
{
	auto pDraw = new DrawFarDlg(true, this);
	FarSet para;
	pDraw->fn_ShowDialog(m_post, para);
	delete pDraw;

	if (para.bCancel) return;
	// load far field data
	if (!m_post.fn_LoadOneFar(m_sPostFile, para.iVoltIdx)) {
		QMessageBox::information(this, "Info", "Load far field file failed.");
		return;
	}
	// insert node in project tree
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Result");
	p = p->child(0);
	QString sName = fn_GetGraphName("2DFar", p);
	auto pSon = new QTreeWidgetItem();
	pSon->setText(0, sName);
	p->addChild(pSon);

	// insert tab in window
	int n = ui.drawPad->count();
	auto grf = new VtkGraph2D(sName);
	ui.drawPad->addTab(grf->fn_GetTab(), sName);
	ui.drawPad->setCurrentIndex(n);

	// create map
	string xlabel, ylabel;
	grf->fn_SetTable(m_post.fn_CreateTable(para, xlabel, ylabel));
	m_vPhoto2D.push_back(grf);

	grf->fn_ToRender(xlabel, ylabel);
}

void MainWindow::fn_DrawNear3D()
{
	auto pDraw = new DrawNearDlg(this);
	NearSet para;
	pDraw->fn_ShowDialog(m_post, para);
	delete pDraw;
	if (para.bCancel) return;
	// load near field data
	if (!m_post.fn_LoadOneNear(m_sPostFile, para.iVoltIndx)) {
		QMessageBox::information(this, "Info", "Load far field file failed.");
		return;
	}
	// insert node in project tree
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Result");
	p = p->child(1);
	QString sName = fn_GetGraphName("3DNear", p);
	auto pSon = new QTreeWidgetItem();
	pSon->setText(0, sName);
	p->addChild(pSon);

	// insert tab in window
	int n = ui.drawPad->count();
	auto grf = new VtkImage3D(sName);
	ui.drawPad->addTab(grf->fn_GetTab(), sName);
	ui.drawPad->setCurrentIndex(n);

	// insert object in graph
	if (m_data.m_solvSet.iAlgType == ALG_FEM) {
		grf->fn_AddTetMsh(m_VtkObj.fn_GetTetMesh());
	} else {
		grf->fn_AddGdmMsh(m_VtkObj.fn_GetGdmMesh());
	}

	// create map
	grf->fn_SetMapper(m_post.fn_CreateMapper(para, m_data, 80.0));

	m_vPhoto3D.push_back(grf);
	grf->fn_ToRender();
}

void MainWindow::fn_DrawCurrent()
{
	auto pDraw = new DrawCurDlg(this);
	CurSet para;
	pDraw->fn_ShowDialog(m_post, para);
	delete pDraw;
	if (para.bCancel) return;
	// load near field data
	if (!m_post.fn_LoadOneCurr(m_sPostFile, para.iVoltIndx)) {
		QMessageBox::information(this, "Info", "Load current file failed.");
		return;
	}
	// load the mesh
	if (!m_post.fn_LoadCurrMsh(m_sPostFile)) {
		QMessageBox::information(this, "Info", "Load mesh of current failed.");
		return;
	}

	// insert node in project tree
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Result");
	p = p->child(2);
	QString sName = fn_GetGraphName("3DCurr", p);
	auto pSon = new QTreeWidgetItem();
	pSon->setText(0, sName);
	p->addChild(pSon);

	// insert tab in window
	int n = ui.drawPad->count();
	auto grf = new VtkImage3D(sName);
	ui.drawPad->addTab(grf->fn_GetTab(), sName);
	ui.drawPad->setCurrentIndex(n);

	// insert object in graph
	if (m_data.m_solvSet.iAlgType == ALG_FEM) {
		grf->fn_AddTetMsh(m_VtkObj.fn_GetTetMesh());
	} else {
		grf->fn_AddGdmMsh(m_VtkObj.fn_GetGdmMesh());
	}
	// create map
	grf->fn_SetMapper(m_post.fn_CreateMapper(para, m_data, 80.0));

	m_vPhoto3D.push_back(grf);
	grf->fn_ToRender();
}

void MainWindow::fn_DrawNetPara()
{
	int iNumPort = m_data.fn_GetPortNum();
	if (iNumPort < 1) return;

	NetSet para;
	auto pDraw = new DrawNetDlg(this, iNumPort);
	pDraw->fn_ShowDialog(para);
	delete pDraw;

	if (para.bCancel) return;
	// load far field data
	if (!m_post.fn_LoadNetPara(m_sPostFile)) {
		QMessageBox::information(this, "Info", "Load network parameter file failed.");
		return;
	}
	// insert node in project tree
	auto p = fn_FindTreeNode(ui.projTree->topLevelItem(0), "Result");
	p = p->child(3);
	QString sName = fn_GetGraphName("Net", p);
	auto pSon = new QTreeWidgetItem();
	pSon->setText(0, sName);
	p->addChild(pSon);
	// insert tab in window
	int n = ui.drawPad->count();
	auto grf = new VtkGraph2D(sName);
	ui.drawPad->addTab(grf->fn_GetTab(), sName);
	ui.drawPad->setCurrentIndex(n);
	// create map
	string xlabel, ylabel;
	grf->fn_SetTable(m_post.fn_CreateTable(para, xlabel, ylabel));
	m_vPhoto2D.push_back(grf);
	grf->fn_ToRender(xlabel, ylabel);
}

void MainWindow::fn_InsertLine()
{
	auto indx = ui.drawPad->currentIndex();
	QString sName = ui.drawPad->tabText(indx);

	for (int i = 0; i < m_vPhoto2D.size(); ++i) {
		if (m_vPhoto2D[i]->fn_GetName() == sName) {
			QString fd = QFileDialog::getOpenFileName(this, "Import Line Data", "", "Data File (*.dat)");
			if ("" == fd) return;
			// insert line
            string sLine = fd.toStdString();
            m_vPhoto2D[indx]->fn_AddLine(sLine);
			break;
		}
	}
}

void MainWindow::fn_ExportLine()
{
	auto indx = ui.drawPad->currentIndex();
	QString sName = ui.drawPad->tabText(indx);

	for (int i = 0; i < m_vPhoto2D.size(); ++i) {
		if (m_vPhoto2D[i]->fn_GetName() == sName) {
			QString fd = QFileDialog::getSaveFileName(this, "Save Line Data", "", "Data File (*.dat)");
			if ("" == fd) return;
			// write the line data
            string sLine = fd.toStdString();
            m_vPhoto2D[i]->fn_SaveLine(sLine);
			break;
		}
	}
}

QTreeWidgetItem* MainWindow::fn_FindTreeNode(QTreeWidgetItem *pNode, QString sName)
{
	for (int i = 0; i < pNode->childCount(); ++i) {
		if (pNode->child(i)->text(0) == sName) {
			return pNode->child(i);
		}
	}
	return nullptr;
}

QString MainWindow::fn_GetGraphName(QString sHead, QTreeWidgetItem *p)
{
	QString sName;

	int i = 1;
	while (i < 10000) {
		sName = sHead + QString::number(i);
		bool bOK = true;
		for (int j = 0; j < p->childCount(); ++j) {
			if (p->child(j)->text(0) == sName) {
				bOK = false;
				break;
			}
		}
		if (bOK) break;
		++i;
	}

	return sName;
}
